package openglsuperbible.Chapter6;

import java.nio.ByteBuffer;
import openglsuperbible.glutils.*;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL12.*;

/**
 *
 * @author Matthew
 */
public class ToonShader {
        public static final int DISPLAY_HEIGHT = 600;
        public static final int DISPLAY_WIDTH = 800;

        public long lastFrame;
       
        private Shader shader;
        
        private MatrixStack modelViewMatrix;  // Modelview Matrix
        private MatrixStack projectionMatrix; // Projection Matrix
        private GLFrustrum frustrum;    // View Frustum
        private GeometryTransform transformPipeline; // Geometry Transform Pipeline
        private GLFrame cameraFrame;
        private GLBatch sphere;
        private int texture;
        private float angle = 0.0f;
        private float vEyeLight[] = { -100.0f, 100.0f, 100.0f };
        
        
        public void initGL()
        {
            // Black background
            glClearColor(0.025f, 0.25f, 0.25f, 1.0f );
            shader = new Shader("ToonShader","ToonShader"); 
             
            glEnable(GL_DEPTH_TEST);
            glEnable(GL_CULL_FACE);
            
            frustrum = new GLFrustrum();               
            modelViewMatrix = new MatrixStack();
            cameraFrame = new GLFrame();
            sphere = GLBatchFactory.makeTorus(.80f, 0.25f, 52, 26); 
            byte[] textureData = {   
                                  (byte)32,  0, 0,
                                  (byte)64,  0, 0,
                                  (byte)128, 0, 0,
                                  (byte)162, 0, 0,
                                  (byte)255, 0, 0
                                                    };
            texture = loadTexture(textureData);

        }
        
              
       

    void render()
    {    
           angle+= 0.05f * getDelta();  

	 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        modelViewMatrix.push();
            float[] mCamera = cameraFrame.getCameraMatrix();
            modelViewMatrix.multiply4(mCamera);

            modelViewMatrix.translate(0, 0, -4.0f);
            modelViewMatrix.rotate(angle, 0.0f, 1.0f, 0.0f); 
            
            // make sure you use the correct target GL_TEXTURE_1D
            glBindTexture(GL_TEXTURE_1D, texture);
            shader.use();          
            shader.setUniformMatrix4("mvpMatrix", false, transformPipeline.getModelViewProjectionMatrix());       
            shader.setUniformMatrix4("mvMatrix", false, transformPipeline.getModelViewMatrix());
            shader.setUniformMatrix3("normalMatrix",false,transformPipeline.getNormalMatrix());        
            shader.setUniform3("vLightPosition",vEyeLight);
            shader.setUniform1i("colorTable",0);
            sphere.draw(shader.getAttributeLocations());     

        modelViewMatrix.pop();

	// Flush drawing commands
	Display.update();  
    }
        
       public void create() throws LWJGLException { 
            //Display    
            Display.setDisplayModeAndFullscreen(new DisplayMode(DISPLAY_WIDTH,DISPLAY_HEIGHT)); 
            
            Display.setTitle("ToonShader");
            Display.create();
            
            //Keyboard         
            Keyboard.create();   
            //Mouse          
            Mouse.setGrabbed(false);
            Mouse.create();  
            //OpenGL      
            initGL();    
            resizeGL();  
        }  
       
        public void resizeGL() {   
            glViewport(0,0,DISPLAY_WIDTH ,DISPLAY_HEIGHT);
            frustrum.setPerspective(35f, Display.getWidth()/ Display.getHeight(),1.0f, 100.0f);
            projectionMatrix = new MatrixStack(frustrum.getProjectionMatrix());
            // Set the transformation pipeline to use the two matrix stacks 
            transformPipeline = new GeometryTransform(modelViewMatrix, projectionMatrix);
        } 
       
            public void destroy() {  
                //Methods already check if created before destroying.  
                Mouse.destroy();   
                Keyboard.destroy();
                Display.destroy();     
            } 
        
            public static void main(String[] args) {
            ToonShader main = null;
            try { 
                 main = new ToonShader(); 
                main.create(); 
                main.run();
            }          
            catch(Exception ex) {  
                 
            }          
            finally { 
                if(main != null) {  
                    main.destroy();
                }         
            }      
        } 
            
            public void run() 
            {
                while (!Display.isCloseRequested() && !Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {              
                       
                render();
       
             
                Display.sync(60);
                Display.setVSyncEnabled(true); 
                
                }
            }
                        
       /** 
	 * Calculate how many milliseconds have passed 
	 * since last frame.
	 * 
	 * @return milliseconds passed since last frame 
	 */
	public int getDelta() {
	    long time = getTime();
	    int delta = (int) (time - lastFrame);
	    lastFrame = time;
	 
	    return delta;
	}
	
	/**
	 * Get the accurate system time
	 * 
	 * @return The system time in milliseconds
	 */
	public long getTime() {
	    return (Sys.getTime() * 1000) / Sys.getTimerResolution();
	}
        
        
        private int loadTexture(byte[] pixles){

            int textureID = glGenTextures();
                glBindTexture(GL_TEXTURE_1D, textureID);
                glTexImage1D(GL_TEXTURE_1D, 0,GL_RGB, 4, 0,GL_RGB, GL_UNSIGNED_BYTE, asByteBuffer(pixles));
                glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
                glBindTexture(GL_TEXTURE_1D, 0);
             return textureID;
        }
        
        public ByteBuffer asByteBuffer(byte[] array)
        {
            ByteBuffer buffer = BufferUtils.createByteBuffer(array.length);
            buffer.put(array);
            buffer.flip();
            return  buffer;
        }
        
}
